package ip

import (
	"errors"
	"fmt"
	"math"
	"net"
	"strconv"
	"strings"
)

func IP2N(ip string) (uint, error) {
	b := net.ParseIP(ip).To4()
	if b == nil {
		return 0, errors.New("invalid ipv4 format")
	}
	return uint(b[0])<<24 | uint(b[1])<<16 | uint(b[2])<<8 | uint(b[3]), nil
}

func N2IP(n uint) (string, error) {
	if n > math.MaxUint32 {
		return "", errors.New("out of range")
	}
	ip := make(net.IP, net.IPv4len)
	ip[0] = byte(n >> 24)
	ip[1] = byte(n >> 16)
	ip[2] = byte(n >> 8)
	ip[3] = byte(n)
	return ip.String(), nil
}

func GetLocalIP() string {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return ""
	}
	for _, addr := range addrs {
		ip, _, _ := net.ParseCIDR(addr.String())
		if s := ip.String(); isLocalIP(s) {
			return s
		}
	}
	return ""
}

var isLocalIP = func() func(ip string) bool {
	_, a, _ := net.ParseCIDR("10.0.0.0/8")
	_, b, _ := net.ParseCIDR("172.16.0.0/12")
	_, c, _ := net.ParseCIDR("192.168.0.0/16")
	return func(ip string) bool {
		i := net.ParseIP(ip)
		if i == nil {
			return false
		}
		return a.Contains(i) || b.Contains(i) || c.Contains(i)
	}
}()

func IsLocalIP(ip string) bool {
	return isLocalIP(ip)
}

func Ip2str(ip string) (string, error) {
	ss := strings.Split(ip, ".")
	if len(ss) != 4 {
		return "", errors.New("format error")
	}
	n := ""
	for i := 0; i < 4; i++ {
		b, err := strconv.Atoi(ss[i])
		if err != nil {
			return "", err
		}
		if b < 0 || b > 255 {
			return "", errors.New("format error")
		}
		n += fmt.Sprintf("%02X", b)
	}
	return n, nil
}

func Str2ip(s string) (string, error) {
	ss := []string{}
	for i := 0; i < 8; i += 2 {
		n, err := strconv.ParseInt(s[i:i+2], 16, 64)
		if err != nil {
			return "", err
		}
		ss = append(ss, strconv.Itoa(int(n)))
	}
	return strings.Join(ss, "."), nil
}
